package com.ihr360.payroll.service.attendanceplan.impl;

import com.ihr360.authority.sharing.annonation.AccessControl;
import com.ihr360.commons.context.session.Ihr360SessionContextHolder;
import com.ihr360.commons.exception.Ihr360Exception;
import com.ihr360.commons.lang.CollectionUtils;
import com.ihr360.commons.model.type.WorkHourType;
import com.ihr360.commons.request.Ihr360SearchBody;
import com.ihr360.event.annonation.RestEventHandler;
import com.ihr360.payroll.dao.AttendancePlanRepository;
import com.ihr360.payroll.dao.OvertimeRuleRepository;
import com.ihr360.payroll.dao.SalaryPlanRepository;
import com.ihr360.payroll.model.dto.attendance.plan.AttendancePlan;
import com.ihr360.payroll.model.dto.attendance.rule.SalaryAdjustWorkHourConfig;
import com.ihr360.payroll.model.dto.attendance.rule.enroll.EnrollAdjustRule;
import com.ihr360.payroll.model.dto.attendance.rule.enroll.EnrollAdjustRuleWorkHourConfig;
import com.ihr360.payroll.model.dto.attendance.rule.overtime.OvertimeRule;
import com.ihr360.payroll.model.dto.attendance.rule.overtime.OvertimeRuleWorkHourConfig;
import com.ihr360.payroll.model.dto.attendance.rule.overtime.SyntheticalDebitRule;
import com.ihr360.payroll.model.dto.attendance.rule.overtime.SyntheticalOverTimeRule;
import com.ihr360.payroll.model.dto.attendance.rule.personalleave.PersonalLeaveRule;
import com.ihr360.payroll.model.dto.attendance.rule.personalleave.PersonalLeaveRuleWorkHourConfig;
import com.ihr360.payroll.model.dto.attendance.rule.sickleave.SickLeaveRule;
import com.ihr360.payroll.model.dto.attendance.rule.sickleave.SickLeaveRuleWorkHourConfig;
import com.ihr360.payroll.model.dto.attendance.rule.synthetical.SyntheticalDebitRuleWorkHourConfig;
import com.ihr360.payroll.model.dto.salary.meta.PayrollConfig;
import com.ihr360.payroll.model.dto.salary.meta.SalaryField;
import com.ihr360.payroll.model.dto.salary.plan.SalaryPlan;
import com.ihr360.payroll.service.alps.profile.OverTimeRuleServiceProfile;
import com.ihr360.payroll.service.attendanceplan.AttendancePlanService;
import com.ihr360.payroll.service.eventhandler.AttendancePlanRestEventListener;
import com.ihr360.payroll.service.payroll.PayrollConfigService;
import com.ihr360.payroll.service.salarytask.SalaryTaskService;
import com.ihr360.payroll.util.SalaryItemSystemNameUtils;
import com.ihr360.rest.reposity.SimpleRestReposity;
import com.ihr360.rest.service.AbstractSimpleRestServiceImpl;
import com.ihr360.validation.ValidationUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author tomtao
 */
@Service
@RestEventHandler(AttendancePlanRestEventListener.class)
@AccessControl()
public class AttendancePlanServiceImpl extends AbstractSimpleRestServiceImpl<AttendancePlan, Long> implements AttendancePlanService {

    @Autowired
    AttendancePlanRepository attendancePlanRepository;
    @Autowired
    SalaryTaskService salaryTaskService;
    @Autowired
    SalaryPlanRepository salaryPlanRepository;
    @Autowired
    OvertimeRuleRepository overtimeRuleRepository;
    @Autowired
    PayrollConfigService payrollConfigService;

    @Override
    public SimpleRestReposity<AttendancePlan, Long> getSimpleRestReposity() {
        return attendancePlanRepository;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AttendancePlan save(AttendancePlan entity) {
        if (entity.getId() != null) {
            AttendancePlan ret = attendancePlanRepository.findOne(entity.getId());
            if (ret.isSystemTemplate()) {
                throw new Ihr360Exception("", "没权限操作！");
            }
            entity = setAttendancePlan(ret, entity);
        } else {
            if (entity.isSystemTemplate()) {
                throw new Ihr360Exception("", "没权限操作！");
            }
            entity = setAttendancePlan(null, entity);
        }
        return super.createOrUpdate(entity.getId(), entity);
    }

    @Override
    public AttendancePlan saveSystemTemplate(AttendancePlan entity) {
        if (entity.getId() != null) {
            AttendancePlan ret = attendancePlanRepository.findOne(entity.getId());
            if (!ret.isSystemTemplate()) {
                throw new Ihr360Exception("", "没权限操作！");
            }
            entity = setAttendancePlan(ret, entity);
        } else {
            if (!entity.isSystemTemplate()) {
                throw new Ihr360Exception("", "没权限操作！");
            }
            entity = setAttendancePlan(null, entity);
        }
        return attendancePlanRepository.save(entity);
    }

    private AttendancePlan setAttendancePlan(AttendancePlan ret, AttendancePlan entity) {
        if (entity.getId() != null) {
            ret.setName(entity.getName());
            ret.setPayrollConfig(entity.getPayrollConfig());
            ret.setRemark(entity.getRemark());
            filterPlanByWorkHourType(entity, entity.getWorkHourType());
            ret.setOvertimeConfig(entity.getOvertimeConfig());
            ret.setEnrollAdjustConfig(entity.getEnrollAdjustConfig());
            ret.setPersonalLeaveConfig(entity.getPersonalLeaveConfig());
            ret.setSickLeaveConfig(entity.getSickLeaveConfig());
            ret.setSalaryAdjustConfig(entity.getSalaryAdjustConfig());
            ret.setStandardBeOnDuty(entity.isStandardBeOnDuty());
            ret.setSyntheticalBeOnDuty(entity.isSyntheticalBeOnDuty());
            return ret;
        } else {
            // 原型没有针对综合扣款规则的设置，通过跟产品确认设置为加班规则的综合工时配置
            OvertimeRuleWorkHourConfig overtimeConfig = entity.getOvertimeConfig();
            SyntheticalDebitRuleWorkHourConfig syntheticalDebitConfig = new SyntheticalDebitRuleWorkHourConfig();
            syntheticalDebitConfig.setsDailyWorkCount(overtimeConfig.getsDailyWorkCount());
            syntheticalDebitConfig.setsMonthWorkCount(overtimeConfig.getsMonthWorkCount());
            entity.setSyntheticalDebitConfig(syntheticalDebitConfig);
            PayrollConfig payrollConfig = entity.getPayrollConfig();
            EnrollAdjustRule enrollAdjustRule = entity.getEnrollAdjustRule();
            PersonalLeaveRule personalLeaveRule = entity.getPersonalLeaveRule();
            SickLeaveRule sickLeaveRule = entity.getSickLeaveRule();
            if(sickLeaveRule.getAdvancedDeductionEnable() == null || !sickLeaveRule.getAdvancedDeductionEnable()) {
                sickLeaveRule.setAdvancedDeductionEnable(false);
                sickLeaveRule.setDeductionBy(null);
                sickLeaveRule.setDeductConfig(null);
            }
            OvertimeRule overtimeRule = entity.getOvertimeRule();
            enrollAdjustRule.setCompanyId(entity.getCompanyId());
            personalLeaveRule.setCompanyId(entity.getCompanyId());
            sickLeaveRule.setCompanyId(entity.getCompanyId());
            overtimeRule.setCompanyId(entity.getCompanyId());
            if (entity.getWorkHourType() == WorkHourType.STANDARD) {
                overtimeRule.setRule(null);
            } else if (entity.getWorkHourType() == WorkHourType.SYNTHETICAL) {
                SyntheticalOverTimeRule rule = overtimeRule.getRule();
                overtimeRule = new OvertimeRule();
                overtimeRule.setBaseRatio(null);
                overtimeRule.setNumOpRatio(null);
                overtimeRule.setRule(rule);
            }
            enrollAdjustRule.setPayrollConfig(payrollConfig);
            personalLeaveRule.setPayrollConfig(payrollConfig);
            sickLeaveRule.setPayrollConfig(payrollConfig);
            overtimeRule.setPayrollConfig(payrollConfig);
            if (overtimeRule.getRule() != null) {
                overtimeRule.getRule().setRule(overtimeRule);
            }
            entity.setEnrollAdjustRule(enrollAdjustRule);
            entity.setPersonalLeaveRule(personalLeaveRule);
            entity.setSickLeaveRule(sickLeaveRule);
            entity.setOvertimeRule(overtimeRule);
            if (entity.getId() == null) {
                entity.setCreatedAt(new Date());
            } else {
                entity.setUpdatedAt(new Date());
            }
            filterPlanByWorkHourType(entity, entity.getWorkHourType());
            return entity;
        }
    }

    private boolean filterPlanByWorkHourType(AttendancePlan attendancePlan, WorkHourType type) {
        OvertimeRuleWorkHourConfig overtimeConfig = attendancePlan.getOvertimeConfig();
        SickLeaveRuleWorkHourConfig sickLeaveConfig = attendancePlan.getSickLeaveConfig();
        PersonalLeaveRuleWorkHourConfig personalLeaveConfig = attendancePlan.getPersonalLeaveConfig();
        EnrollAdjustRuleWorkHourConfig enrollAdjustConfig = attendancePlan.getEnrollAdjustConfig();
        SalaryAdjustWorkHourConfig salaryAdjustConfig = attendancePlan.getSalaryAdjustConfig();
        OvertimeRule overtimeRule = attendancePlan.getOvertimeRule();
        Integer defaultDailyWorkCount = new Double(OverTimeRuleServiceProfile.DEFAULT_VAL.DAILY_WORK_HOURS * 100).intValue();
        Integer defaultMonthWorkCount = new Double(OverTimeRuleServiceProfile.DEFAULT_VAL.MONTHLY_WORK_HOURS * 100).intValue();
        Integer defaultSDailyWorkCount = new Double(OverTimeRuleServiceProfile.DEFAULT_VAL.DAILY_WORK_HOURS * 100).intValue();
        Integer defaultSMonthWorkCount = new Double(OverTimeRuleServiceProfile.DEFAULT_VAL.MONTHLY_WORK_HOURS * 100).intValue();
        switch (type) {
            case STANDARD:
                // 删除综合工时数据
                if (overtimeConfig != null) {
                    overtimeConfig.setsMonthWorkCount(null);
                    overtimeConfig.setsDailyWorkCount(null);
                    overtimeConfig.setDailyWorkCount(overtimeConfig.getDailyWorkCount() == null ? defaultDailyWorkCount : overtimeConfig.getDailyWorkCount());
                    overtimeConfig.setMonthWorkCount(overtimeConfig.getMonthWorkCount() == null ? defaultMonthWorkCount : overtimeConfig.getMonthWorkCount());
                    attendancePlan.setOvertimeConfig(overtimeConfig);
                }
                if (sickLeaveConfig != null) {
                    sickLeaveConfig.setsDailyWorkCount(null);
                    sickLeaveConfig.setsMonthWorkCount(null);
                    sickLeaveConfig.setDailyWorkCount(sickLeaveConfig.getDailyWorkCount() == null ? defaultDailyWorkCount : sickLeaveConfig.getDailyWorkCount());
                    sickLeaveConfig.setMonthWorkCount(sickLeaveConfig.getMonthWorkCount() == null ? defaultMonthWorkCount : sickLeaveConfig.getMonthWorkCount());
                    attendancePlan.setSickLeaveConfig(sickLeaveConfig);
                }
                if (personalLeaveConfig != null) {
                    personalLeaveConfig.setsDailyWorkCount(null);
                    personalLeaveConfig.setsMonthWorkCount(null);
                    personalLeaveConfig.setDailyWorkCount(personalLeaveConfig.getDailyWorkCount() == null ? defaultDailyWorkCount : personalLeaveConfig.getDailyWorkCount());
                    personalLeaveConfig.setMonthWorkCount(personalLeaveConfig.getMonthWorkCount() == null ? defaultMonthWorkCount : personalLeaveConfig.getMonthWorkCount());
                    attendancePlan.setPersonalLeaveConfig(personalLeaveConfig);
                }
                if (enrollAdjustConfig != null) {
                    enrollAdjustConfig.setsDailyWorkCount(null);
                    enrollAdjustConfig.setsMonthWorkCount(null);
                    enrollAdjustConfig.setDailyWorkCount(enrollAdjustConfig.getDailyWorkCount() == null ? defaultDailyWorkCount : enrollAdjustConfig.getDailyWorkCount());
                    enrollAdjustConfig.setMonthWorkCount(enrollAdjustConfig.getMonthWorkCount() == null ? defaultMonthWorkCount : enrollAdjustConfig.getMonthWorkCount());
                    attendancePlan.setEnrollAdjustConfig(enrollAdjustConfig);
                }
                if (salaryAdjustConfig != null) {
                    salaryAdjustConfig.setsDailyWorkCount(null);
                    salaryAdjustConfig.setsMonthWorkCount(null);
                    salaryAdjustConfig.setDailyWorkCount(salaryAdjustConfig.getDailyWorkCount() == null ? defaultDailyWorkCount : salaryAdjustConfig.getDailyWorkCount());
                    salaryAdjustConfig.setMonthWorkCount(salaryAdjustConfig.getMonthWorkCount() == null ? defaultMonthWorkCount : salaryAdjustConfig.getMonthWorkCount());
                    attendancePlan.setSalaryAdjustConfig(salaryAdjustConfig);
                }
                SyntheticalOverTimeRule rule = overtimeRule.getRule();
                if (rule != null) {
                    rule.setWorkCount(null);
                    rule.setDebitRule(null);
                    rule.setStandard(null);
                    rule.setReferenceFields(null);
                    rule.setFixedBase(null);
                    rule.setBaseRatio(null);
                    rule.setNumOpRatio(null);
                    rule.setHolidayRatio(null);
                    rule.setBasementType(null);
                    overtimeRule.setRule(rule);
                }
                attendancePlan.setOvertimeRule(overtimeRule);
                break;
            case SYNTHETICAL:
                // 删除标准工时数据
                if (overtimeConfig != null) {
                    overtimeConfig.setDailyWorkCount(null);
                    overtimeConfig.setMonthWorkCount(null);
                    overtimeConfig.setsDailyWorkCount(overtimeConfig.getsDailyWorkCount() == null ? defaultSDailyWorkCount : overtimeConfig.getsDailyWorkCount());
                    overtimeConfig.setsMonthWorkCount(overtimeConfig.getsMonthWorkCount() == null ? 2175 : overtimeConfig.getsMonthWorkCount());
                    attendancePlan.setOvertimeConfig(overtimeConfig);
                }
                if (sickLeaveConfig != null) {
                    sickLeaveConfig.setDailyWorkCount(null);
                    sickLeaveConfig.setMonthWorkCount(null);
                    sickLeaveConfig.setsDailyWorkCount(sickLeaveConfig.getsDailyWorkCount() == null ? defaultSDailyWorkCount : sickLeaveConfig.getsDailyWorkCount());
                    sickLeaveConfig.setsMonthWorkCount(sickLeaveConfig.getsMonthWorkCount() == null ? defaultSMonthWorkCount : sickLeaveConfig.getsMonthWorkCount());
                    attendancePlan.setSickLeaveConfig(sickLeaveConfig);
                }
                if (personalLeaveConfig != null) {
                    personalLeaveConfig.setDailyWorkCount(null);
                    personalLeaveConfig.setMonthWorkCount(null);
                    personalLeaveConfig.setsDailyWorkCount(personalLeaveConfig.getsDailyWorkCount() == null ? defaultSDailyWorkCount : personalLeaveConfig.getsDailyWorkCount());
                    personalLeaveConfig.setsMonthWorkCount(personalLeaveConfig.getsMonthWorkCount() == null ? defaultSMonthWorkCount : personalLeaveConfig.getsMonthWorkCount());
                    attendancePlan.setPersonalLeaveConfig(personalLeaveConfig);
                }
                if (enrollAdjustConfig != null) {
                    enrollAdjustConfig.setDailyWorkCount(null);
                    enrollAdjustConfig.setMonthWorkCount(null);
                    enrollAdjustConfig.setsDailyWorkCount(enrollAdjustConfig.getsDailyWorkCount() == null ? defaultSDailyWorkCount : enrollAdjustConfig.getsDailyWorkCount());
                    enrollAdjustConfig.setsMonthWorkCount(enrollAdjustConfig.getsMonthWorkCount() == null ? defaultSMonthWorkCount : enrollAdjustConfig.getsMonthWorkCount());
                    attendancePlan.setEnrollAdjustConfig(enrollAdjustConfig);
                }
                if (salaryAdjustConfig != null) {
                    salaryAdjustConfig.setDailyWorkCount(null);
                    salaryAdjustConfig.setMonthWorkCount(null);
                    salaryAdjustConfig.setsDailyWorkCount(salaryAdjustConfig.getsDailyWorkCount() == null ? defaultSDailyWorkCount : salaryAdjustConfig.getsDailyWorkCount());
                    salaryAdjustConfig.setsMonthWorkCount(salaryAdjustConfig.getsMonthWorkCount() == null ? defaultSMonthWorkCount : salaryAdjustConfig.getsMonthWorkCount());
                    attendancePlan.setSalaryAdjustConfig(salaryAdjustConfig);
                }
                overtimeRule.setNumOpRatio(null);
                overtimeRule.setFixedBase(null);
                overtimeRule.setBasementType(null);
                overtimeRule.setBaseRatio(null);
                overtimeRule.setEnableSubsectionPaid(false);
                overtimeRule.setWeekendRatio(null);
                overtimeRule.setHolidayRatio(null);
                overtimeRule.setReferenceFields(new ArrayList<>());
                overtimeRule.setRemark(null);
                attendancePlan.setOvertimeRule(overtimeRule);
                break;
            case ALL:
                overtimeConfig.setDailyWorkCount(overtimeConfig.getDailyWorkCount() == null ? defaultDailyWorkCount : overtimeConfig.getDailyWorkCount());
                overtimeConfig.setMonthWorkCount(overtimeConfig.getMonthWorkCount() == null ? defaultMonthWorkCount : overtimeConfig.getMonthWorkCount());
                overtimeConfig.setsDailyWorkCount(overtimeConfig.getsDailyWorkCount() == null ? defaultSDailyWorkCount : overtimeConfig.getsDailyWorkCount());
                overtimeConfig.setsMonthWorkCount(overtimeConfig.getsMonthWorkCount() == null ? defaultSMonthWorkCount : overtimeConfig.getsMonthWorkCount());

                sickLeaveConfig.setDailyWorkCount(sickLeaveConfig.getDailyWorkCount() == null ? defaultDailyWorkCount : sickLeaveConfig.getDailyWorkCount());
                sickLeaveConfig.setMonthWorkCount(sickLeaveConfig.getMonthWorkCount() == null ? defaultMonthWorkCount : sickLeaveConfig.getMonthWorkCount());
                sickLeaveConfig.setsDailyWorkCount(sickLeaveConfig.getsDailyWorkCount() == null ? defaultSDailyWorkCount : sickLeaveConfig.getsDailyWorkCount());
                sickLeaveConfig.setsMonthWorkCount(sickLeaveConfig.getsMonthWorkCount() == null ? defaultSMonthWorkCount : sickLeaveConfig.getsMonthWorkCount());

                personalLeaveConfig.setDailyWorkCount(personalLeaveConfig.getDailyWorkCount() == null ? defaultDailyWorkCount : personalLeaveConfig.getDailyWorkCount());
                personalLeaveConfig.setMonthWorkCount(personalLeaveConfig.getMonthWorkCount() == null ? defaultMonthWorkCount : personalLeaveConfig.getMonthWorkCount());
                personalLeaveConfig.setsDailyWorkCount(personalLeaveConfig.getsDailyWorkCount() == null ? defaultSDailyWorkCount : personalLeaveConfig.getsDailyWorkCount());
                personalLeaveConfig.setsMonthWorkCount(personalLeaveConfig.getsMonthWorkCount() == null ? 2175 : personalLeaveConfig.getsMonthWorkCount());

                enrollAdjustConfig.setDailyWorkCount(enrollAdjustConfig.getDailyWorkCount() == null ? defaultDailyWorkCount : enrollAdjustConfig.getDailyWorkCount());
                enrollAdjustConfig.setMonthWorkCount(enrollAdjustConfig.getMonthWorkCount() == null ? defaultMonthWorkCount : enrollAdjustConfig.getMonthWorkCount());
                enrollAdjustConfig.setsDailyWorkCount(enrollAdjustConfig.getsDailyWorkCount() == null ? defaultSDailyWorkCount : enrollAdjustConfig.getsDailyWorkCount());
                enrollAdjustConfig.setsMonthWorkCount(enrollAdjustConfig.getsMonthWorkCount() == null ? defaultSMonthWorkCount : enrollAdjustConfig.getsMonthWorkCount());

                if (salaryAdjustConfig == null) {
                    salaryAdjustConfig = new SalaryAdjustWorkHourConfig();
                }
                salaryAdjustConfig.setDailyWorkCount(salaryAdjustConfig.getDailyWorkCount() == null ? defaultDailyWorkCount : salaryAdjustConfig.getDailyWorkCount());
                salaryAdjustConfig.setMonthWorkCount(salaryAdjustConfig.getMonthWorkCount() == null ? defaultMonthWorkCount : salaryAdjustConfig.getMonthWorkCount());
                salaryAdjustConfig.setsDailyWorkCount(salaryAdjustConfig.getsDailyWorkCount() == null ? defaultSDailyWorkCount : salaryAdjustConfig.getsDailyWorkCount());
                salaryAdjustConfig.setsMonthWorkCount(salaryAdjustConfig.getsMonthWorkCount() == null ? defaultSMonthWorkCount : salaryAdjustConfig.getsMonthWorkCount());
                break;
            default:
                ValidationUtils.reject("", "没有对应工时.");
                break;
        }
        return true;
    }

    @Override
    public boolean isDuplicateName(String name, Long id) {
        String companyId = Ihr360SessionContextHolder.getCompanyId();
        return (id != null && id > 0) ?
                attendancePlanRepository.countByCompanyIdAndNameAndIdNot(companyId, name, id) > 0 :
                attendancePlanRepository.countByCompanyIdAndName(companyId, name) > 0;
    }

    @Override
    public boolean isDuplicateNameSystemTemplate(String name, Long id) {
        return (id != null && id > 0) ?
                attendancePlanRepository.countByIsSystemTemplateAndNameAndIdNot(true, name, id) > 0 :
                attendancePlanRepository.countByIsSystemTemplateAndName(true, name) > 0;
    }

    @Override
    public List<AttendancePlan> list() {
        String companyId = Ihr360SessionContextHolder.getCompanyId();
        return attendancePlanRepository.findByCompanyId(companyId);
    }

    @Override
    public boolean isReferenced(Long id, boolean haveEnable) {
        return haveEnable ? salaryPlanRepository.countByEnabledAndPayrollConfigId(true, id) > 0
                : salaryPlanRepository.countByAttendancePlanId(id) > 0;
    }

    @Override
    public List<SalaryPlan> getIsPayPeriodSalaryPlanList(String companyId, Long id) {
        return salaryTaskService.getActiveAttendancePlanIds(companyId, id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean changeWorkHourType(Long id, WorkHourType type) {
        AttendancePlan attendancePlan = super.getOne(id);
        if (attendancePlan == null) {
            ValidationUtils.reject("", "没有找到方案.");
        }
        if (filterPlanByWorkHourType(attendancePlan, type)) {
            super.createOrUpdate(attendancePlan.getId(), attendancePlan);
            OvertimeRule overtimeRule = attendancePlan.getOvertimeRule();
            overtimeRuleRepository.save(overtimeRule);

        }
        return true;
    }

    @Override
    public boolean changeSystemTemplateWorkHourType(Long id, WorkHourType type) {
        AttendancePlan attendancePlan = attendancePlanRepository.findOne(id);
        if (!attendancePlan.isSystemTemplate()) {
            throw new Ihr360Exception("", "您没有权限操作．");
        }
        if (filterPlanByWorkHourType(attendancePlan, type)) {
            attendancePlanRepository.save(attendancePlan);
            OvertimeRule overtimeRule = attendancePlan.getOvertimeRule();
            overtimeRuleRepository.save(overtimeRule);

        }
        return true;
    }

    @Override
    public Page<AttendancePlan> searchSystemTemplate(Ihr360SearchBody<AttendancePlan> searchBody) {
        Specification<AttendancePlan> specification = (root, cq, cb) -> {
            Predicate predicate = cb.conjunction();
            List<Expression<Boolean>> expressionList = predicate.getExpressions();
            expressionList.add(cb.equal(root.get("isSystemTemplate").as(boolean.class), true));
            if (searchBody.getSpecification() != null) {
                Predicate searchPredicate = searchBody.getSpecification().toPredicate(root, cq, cb);
                if (searchPredicate != null) {
                    return cb.and(predicate, searchBody.getSpecification().toPredicate(root, cq, cb));
                }
            }
            return predicate;
        };
        return attendancePlanRepository.findAll(specification, searchBody.getPageable());
    }

    @Override
    public AttendancePlan getOneSystemTemplate(Long id) {
        AttendancePlan attendancePlan = attendancePlanRepository.findOne(id);
        if (attendancePlan == null) {
            return null;
        }
        if (!attendancePlan.isSystemTemplate()) {
            throw new Ihr360Exception("", "没有权限操作.");
        }
        return attendancePlan;
    }

    @Override
    public boolean deleteSystemTemplateById(Long id) {
        AttendancePlan attendancePlan = attendancePlanRepository.findOne(id);
        if (attendancePlan == null) {
            return true;
        }
        if (!attendancePlan.isSystemTemplate()) {
            throw new Ihr360Exception("", "您没有权限操作");
        }
        attendancePlanRepository.delete(id);
        return true;
    }

    @Override
    public boolean deleteById(Long id) {
        AttendancePlan attendancePlan = super.getOne(id);
        if (attendancePlan == null) {
            return true;
        }
        if (attendancePlan.isSystemTemplate()) {
            throw new Ihr360Exception("", "您没有权限操作");
        }
        super.deleteOne(id);
        return true;
    }

    @Override
    public AttendancePlan getOneContainSystemTemplate(Long id) {
        AttendancePlan attendancePlan = attendancePlanRepository.findOne(id);
        if (attendancePlan.isSystemTemplate()) {
            return attendancePlan;
        }
        return super.getOne(id);
    }

    @Override
    public AttendancePlan cloneAttendancePlan(Long id) {
        String companyId = Ihr360SessionContextHolder.getCompanyId();
        AttendancePlan clonedAttendancePlan = getOneContainSystemTemplate(id);
        if (clonedAttendancePlan == null) {
            throw new Ihr360Exception("", "没有获取到方案");
        }

        //1.封装新的考勤计薪规则
        AttendancePlan attendancePlan = new AttendancePlan();
        BeanUtils.copyProperties(clonedAttendancePlan, attendancePlan);
        attendancePlan.setId(null);
        attendancePlan.setCompanyId(companyId);
        attendancePlan.setSystemTemplate(false);

        //2.克隆上游对象
        PayrollConfig clonedPayrollConfig = clonedAttendancePlan.getPayrollConfig();
        if (clonedPayrollConfig == null) {
            throw new Ihr360Exception("", "考勤方案没有配置薪资表，无法复制");
        }
        PayrollConfig payrollConfig = payrollConfigService.clonePayrollConfig(clonedPayrollConfig.getId());
        attendancePlan.setPayrollConfig(payrollConfig);
        List<SalaryField> salaryFieldList = attendancePlan.getPayrollConfig().getFields();
        Map<String, SalaryField> salaryFieldMap = salaryFieldList.stream().collect(Collectors.toMap(SalaryField::getCode, salaryField -> salaryField));

        OvertimeRule overtimeRule = new OvertimeRule();
        BeanUtils.copyProperties(clonedAttendancePlan.getOvertimeRule(), overtimeRule);
        overtimeRule.setId(null);
        overtimeRule.setPayrollConfig(attendancePlan.getPayrollConfig());
        if (!CollectionUtils.isEmpty(overtimeRule.getReferenceFields())) {
            overtimeRule.setReferenceFields(getReferenceSalaryFields(salaryFieldMap, overtimeRule.getReferenceFields()));
        }
        if (overtimeRule.getRule() != null) {
            SyntheticalOverTimeRule rule = new SyntheticalOverTimeRule();
            BeanUtils.copyProperties(overtimeRule.getRule(), rule);
            rule.setReferenceFields(getReferenceSalaryFields(salaryFieldMap, rule.getReferenceFields()));
            rule.setRule(overtimeRule);
            if (rule.getDebitRule() != null) {
                SyntheticalDebitRule debitRule = new SyntheticalDebitRule();
                BeanUtils.copyProperties(rule.getDebitRule(), debitRule);
                debitRule.setReferenceFields(getReferenceSalaryFields(salaryFieldMap, debitRule.getReferenceFields()));
                debitRule.setRule(rule);
                rule.setDebitRule(debitRule);
            }
            overtimeRule.setRule(rule);
        }
        attendancePlan.setOvertimeRule(overtimeRule);

        SickLeaveRule sickLeaveRule = new SickLeaveRule();
        BeanUtils.copyProperties(clonedAttendancePlan.getSickLeaveRule(), sickLeaveRule);
        sickLeaveRule.setId(null);
        sickLeaveRule.setPayrollConfig(attendancePlan.getPayrollConfig());
        sickLeaveRule.setReferenceFields(getReferenceSalaryFields(salaryFieldMap, sickLeaveRule.getReferenceFields()));
        attendancePlan.setSickLeaveRule(sickLeaveRule);

        PersonalLeaveRule personalLeaveRule = new PersonalLeaveRule();
        BeanUtils.copyProperties(clonedAttendancePlan.getPersonalLeaveRule(), personalLeaveRule);
        personalLeaveRule.setId(null);
        personalLeaveRule.setPayrollConfig(attendancePlan.getPayrollConfig());
        personalLeaveRule.setReferenceFields(getReferenceSalaryFields(salaryFieldMap, personalLeaveRule.getReferenceFields()));
        attendancePlan.setPersonalLeaveRule(personalLeaveRule);

        EnrollAdjustRule enrollAdjustRule = new EnrollAdjustRule();
        BeanUtils.copyProperties(clonedAttendancePlan.getEnrollAdjustRule(), enrollAdjustRule);
        enrollAdjustRule.setId(null);
        enrollAdjustRule.setPayrollConfig(attendancePlan.getPayrollConfig());
        enrollAdjustRule.setReferenceFields(getReferenceSalaryFields(salaryFieldMap, enrollAdjustRule.getReferenceFields()));
        attendancePlan.setEnrollAdjustRule(enrollAdjustRule);

        OvertimeRuleWorkHourConfig overtimeConfig = new OvertimeRuleWorkHourConfig();
        BeanUtils.copyProperties(clonedAttendancePlan.getOvertimeConfig(), overtimeConfig);
        overtimeConfig.setId(null);
        attendancePlan.setOvertimeConfig(overtimeConfig);

        SickLeaveRuleWorkHourConfig sickLeaveConfig = new SickLeaveRuleWorkHourConfig();
        BeanUtils.copyProperties(clonedAttendancePlan.getSickLeaveConfig(), sickLeaveConfig);
        sickLeaveConfig.setId(null);
        attendancePlan.setSickLeaveConfig(sickLeaveConfig);

        PersonalLeaveRuleWorkHourConfig personalLeaveConfig = new PersonalLeaveRuleWorkHourConfig();
        BeanUtils.copyProperties(clonedAttendancePlan.getPersonalLeaveConfig(), personalLeaveConfig);
        personalLeaveConfig.setId(null);
        attendancePlan.setPersonalLeaveConfig(personalLeaveConfig);

        EnrollAdjustRuleWorkHourConfig enrollAdjustConfig = new EnrollAdjustRuleWorkHourConfig();
        BeanUtils.copyProperties(clonedAttendancePlan.getEnrollAdjustConfig(), enrollAdjustConfig);
        enrollAdjustConfig.setId(null);
        attendancePlan.setEnrollAdjustConfig(enrollAdjustConfig);


        SyntheticalDebitRuleWorkHourConfig debitConfig = clonedAttendancePlan.getSyntheticalDebitConfig();
        if (debitConfig!=null){
            SyntheticalDebitRuleWorkHourConfig syntheticalDebitConfig = new SyntheticalDebitRuleWorkHourConfig();
            BeanUtils.copyProperties(debitConfig, syntheticalDebitConfig);
            syntheticalDebitConfig.setId(null);
            attendancePlan.setSyntheticalDebitConfig(syntheticalDebitConfig);
        }

        SalaryAdjustWorkHourConfig salaryAdjustConfig = new SalaryAdjustWorkHourConfig();
        BeanUtils.copyProperties(clonedAttendancePlan.getSalaryAdjustConfig(), salaryAdjustConfig);
        salaryAdjustConfig.setId(null);
        attendancePlan.setSalaryAdjustConfig(salaryAdjustConfig);

        //3．生成名称
        String name;
        while (true) {
            // 获取不重复名称
            name = SalaryItemSystemNameUtils.getSystemName(attendancePlan.getName());
            if (!isDuplicateName(name, null)) {
                break;
            }
        }
        attendancePlan.setName(name);

        //4．保存
        return super.create(attendancePlan);
    }

    @Override
    public List<Long> getSystemTemplateIdList() {
        List<AttendancePlan> attendancePlanList = attendancePlanRepository.findByIsSystemTemplate(true);
        return attendancePlanList.stream().map(AttendancePlan::getId).collect(Collectors.toList());
    }

    private List<SalaryField> getReferenceSalaryFields(Map<String, SalaryField> salaryFieldMap, List<SalaryField> referenceSalaryFields) {
        if (CollectionUtils.isEmpty(referenceSalaryFields)) {
            return Collections.emptyList();
        }
        List<SalaryField> result = new ArrayList<>();
        referenceSalaryFields.forEach(salaryField -> {
            String code = salaryField.getCode();
            if (salaryFieldMap.containsKey(code)) {
                result.add(salaryFieldMap.get(code));
            }
        });
        return result;
    }
}
